# -*- coding: utf-8 -*-

import bpy, bmesh

def toggle_mesh_select_mode(type='VERT', bm=None):
    # https://blender.stackexchange.com/questions/174551/how-to-select-vertices-then-convert-to-face-selection-with-python
    if 'VERT' == type.upper():
        use_mode = (True, False, False)
    elif 'EDGE' == type.upper():
        use_mode = (False, True, False)
    elif 'FACE' == type.upper():
        use_mode = (False, False, True)
    else:
        use_mode = None
    if use_mode:
        if bm:
            bm.select_mode = {'VERT', 'EDGE', 'FACE'}
            bm.select_flush_mode()
        bpy.context.tool_settings.mesh_select_mode = use_mode

class ChesterfieldLeatherModel(object):
    def __add_plane(self, size=4, location=(0, 0, 0)):
        bpy.ops.mesh.primitive_plane_add(size=4, enter_editmode=False, align='WORLD')
        return(bpy.context.active_object)

    def __select_similar_func(self, type, bm, index):
        # 选择相似顶点
        bpy.ops.mesh.select_all(action='DESELECT')
        if type in ['EDGE']:
            toggle_mesh_select_mode('VERT')
            bm.verts.ensure_lookup_table()
            bm.verts[index].select = True
        elif type in ['LENGTH']:
            toggle_mesh_select_mode('EDGE')
            bm.edges.ensure_lookup_table()
            bm.edges[index].select = True
        bpy.ops.mesh.select_similar(type=type, threshold=0.01)

    def __select_similar_length_edges(self, object, index):
        toggle_mesh_select_mode('VERT')
        bpy.ops.mesh.select_all(action='DESELECT')
        bm = bmesh.from_edit_mesh(object.data)
        bm.edges.ensure_lookup_table()
        bm.edges[index].select = True
        bpy.ops.mesh.select_similar(type='LENGTH', threshold=0.01)

    def __select_verts_list(self, bm, index_list):
        toggle_mesh_select_mode('VERT')
        bpy.ops.mesh.select_all(action='DESELECT')
        bm.verts.ensure_lookup_table()
        for index in index_list:
            bm.verts[index].select = True

    def __transform_bevel_extrude(self, hole_height, hole_size, hole_depth):
        bpy.ops.transform.translate(value=(0, 0, -hole_height), orient_type='LOCAL')
        bpy.ops.mesh.bevel(offset=hole_size, offset_pct=0, vertex_only=True)
        toggle_mesh_select_mode('FACE')
        bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0, 0, -hole_depth), 'orient_type':'LOCAL'})

    def __finish_job(self, subdivision=1):
        bpy.ops.object.editmode_toggle()
        bpy.ops.object.shade_smooth()
        bpy.ops.object.subdivision_set(level=subdivision, relative=False)

    def type_01(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分两次
        2. 【尖分面】：Face > Poke Faces, Ctrl+F > Poke Faces
        3. 【三角面->四边面】：Face > Tris to Quads, Ctrl+F > Tris to Quads, Alt+J
        4. 【尖分面】两次
        5. 选择顶点：Select > Select Similar > Amount of connecting edges, Shift+G > Amount of connecting edges
        6. 向下移动
        7. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        8. 【挤出】：向下挤出，E+Z
        9. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        bpy.ops.mesh.poke()
        bpy.ops.mesh.tris_convert_to_quads()
        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()

        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_similar_func('EDGE', bm, 8)
        self.__transform_bevel_extrude(hole_height, hole_size, hole_depth)
        self.__finish_job()
        return(obj)

    def type_02(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分两次
        2. 【面三角化】：Face > Triangulate Faces, Ctrl+F > Triangulate Faces, Ctrl+T
        3. 【尖分面】：Face > Poke Faces, Ctrl+F > Poke Faces
        4. 【三角面->四边面】：Face > Tris to Quads, Ctrl+F > Tris to Quads, Alt+J
        5. 【尖分面】两次
        6. 选择顶点：Select > Select Similar > Amount of connecting edges, Shift+G > Amount of connecting edges
        7. 向下移动
        8. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        9. 【挤出】：向下挤出，E+Z
        10. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        bpy.ops.mesh.poke()
        bpy.ops.mesh.tris_convert_to_quads()
        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()

        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_similar_func('EDGE', bm, 8)
        self.__transform_bevel_extrude(hole_height, hole_size, hole_depth)
        self.__finish_job()
        return(obj)

    def type_03(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分两次
        2. 【尖分面】三次：Face > Poke Faces, Ctrl+F > Poke Faces
        3. 选择顶点：Select > Select Similar > Amount of connecting edges, Shift+G > Amount of connecting edges
        4. 向下移动
        5. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        6. 【挤出】：向下挤出，E+Z
        7. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()

        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_similar_func('EDGE', bm, 8)
        self.__transform_bevel_extrude(hole_height, hole_size, hole_depth)
        self.__finish_job()
        return(obj)

    def type_04(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分两次
        2. 【尖分面】三次：Face > Poke Faces, Ctrl+F > Poke Faces
        3. 选择顶点：Select > Select Similar > Amount of connecting edges, Shift+G > Amount of connecting edges
        4. 向下移动
        5. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        6. 【挤出】：向下挤出，E+Z
        7. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()
        bpy.ops.mesh.poke()

        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_similar_func('EDGE', bm, 25)
        self.__transform_bevel_extrude(hole_height, hole_size, hole_depth)
        self.__finish_job()
        return(obj)

    def type_05(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分三次
        2. 选择连接顶点
            1. 选择除边缘外的顶点
            2. 【间隔式弃选】：Select > Checker Deselect
            3. 【连接顶点对】：Vertex > Connect Vertex Pairs, Ctrl+V > Connect Vertex Pairs
        3. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        4. 向下移动
        5. 【挤出】：向下挤出，E+Z
        6. 【塌陷边线&面】：Mesh > Delete > Collapse Edges & Faces, X > Collapse Edges & Faces
        7. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        select_list = [8, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_verts_list(bm, select_list)

        bpy.ops.mesh.select_nth()
        bpy.ops.mesh.vert_connect()

        bpy.ops.mesh.bevel(offset=hole_size, offset_pct=0, vertex_only=True)
        bpy.ops.transform.translate(value=(0, 0, -hole_height), orient_type='LOCAL')
        toggle_mesh_select_mode('FACE')
        bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0, 0, -hole_depth), 'orient_type':'LOCAL'})
        bpy.ops.mesh.edge_collapse()

        self.__finish_job(2)
        return(obj)

    def type_06(self, total_size=4, hole_height=0.2, hole_size=0.05, edge_offset=0.01, edge_shrink=0.02, edge_height=0.05):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分三次
        2. 选择连接顶点
            1. 选择除边缘外的顶点
            2. 【间隔式弃选】：Select > Checker Deselect
            3. 【连接顶点对】：Vertex > Connect Vertex Pairs, Ctrl+V > Connect Vertex Pairs
        3. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        4. 【挤出】：向下挤出，E+Z
        5. 【塌陷边线&面】：Mesh > Delete > Collapse Edges & Faces, X > Collapse Edges & Faces
        6. 选择边：视频中选择方式可优化为按相似长度选择边，再循环边选择
            1. 选择一条斜边
            2. Select > Select Similar > Length, Shift+G > Length
            3. Select > Select Loops > Edge Loops
        7. 【边线倒角】：3段，Edge > Bevel Edges, Ctrl+B
        8. 移动倒角边：
            1. 减选一段：Select > Select More/Less > Less, Ctrl+-
            2. 【法向缩放】：往里，Mesh > Transform > Shrink Fatten, Alt+S
            3. 往上移动至突起
        9. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        select_list = [8, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_verts_list(bm, select_list)

        bpy.ops.mesh.select_nth()
        bpy.ops.mesh.vert_connect()

        bpy.ops.mesh.bevel(offset=hole_size, offset_pct=0, vertex_only=True)
        toggle_mesh_select_mode('FACE')
        bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0, 0, -hole_height), 'orient_type':'LOCAL'})
        bpy.ops.mesh.edge_collapse()

        self.__select_similar_func('LENGTH', bm, 220)
        bpy.ops.mesh.loop_multi_select(ring=False)

        bpy.ops.mesh.bevel(offset=edge_offset, offset_pct=0, segments=3, vertex_only=False)
        bpy.ops.mesh.select_less()
        bpy.ops.transform.shrink_fatten(value=edge_shrink)
        bpy.ops.transform.translate(value=(0, 0, edge_height), orient_type='LOCAL')

        self.__finish_job(2)
        return(obj)

    def type_07(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2, edge_offset=0.01, edge_shrink=-0.02):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分三次
        2. 选择连接顶点
            1. 选择除边缘外的顶点
            2. 【间隔式弃选】：Select > Checker Deselect
            3. 【连接顶点对】：Vertex > Connect Vertex Pairs, Ctrl+V > Connect Vertex Pairs
        3. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        4. 向下移动
        5. 【挤出】：向下挤出，E+Z
        6. 【塌陷边线&面】：Mesh > Delete > Collapse Edges & Faces, X > Collapse Edges & Faces
        7. 选择边：视频中选择方式可优化为按相似长度选择边，再循环边选择
            1. 选择一条斜边
            2. Select > Select Similar > Length, Shift+G > Length
            3. Select > Select Loops > Edge Loops
        8. 【边线倒角】：2段，Edge > Bevel Edges, Ctrl+B
        9. 移动倒角边：
            1. 减选一段：Select > Select More/Less > Less, Ctrl+-
            2. 【法向缩放】：往里，Mesh > Transform > Shrink Fatten, Alt+S
        10. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        select_list = [8, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_verts_list(bm, select_list)

        bpy.ops.mesh.select_nth()
        bpy.ops.mesh.vert_connect()

        bpy.ops.mesh.bevel(offset=hole_size, offset_pct=0, vertex_only=True)
        toggle_mesh_select_mode('FACE')
        bpy.ops.transform.translate(value=(0, 0, -hole_height), orient_type='LOCAL')
        bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0, 0, -hole_depth), 'orient_type':'LOCAL'})
        bpy.ops.mesh.edge_collapse()

        self.__select_similar_func('LENGTH', bm, 220)
        bpy.ops.mesh.loop_multi_select(ring=False)

        bpy.ops.mesh.bevel(offset=edge_offset, offset_pct=0, segments=2, vertex_only=False)
        bpy.ops.mesh.select_less()
        bpy.ops.transform.shrink_fatten(value=edge_shrink)

        self.__finish_job(2)
        return(obj)

    def type_08(self, total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2, edge_offset=0.01, edge_shrink=0.02):
        '''
        1. 创建大小为4的平面，进入编辑模式，细分三次
        2. 选择连接顶点
            1. 选择除边缘外的顶点
            2. 【间隔式弃选】：Select > Checker Deselect
            3. 【连接顶点对】：Vertex > Connect Vertex Pairs, Ctrl+V > Connect Vertex Pairs
        3. 【顶点倒角】：Vertex > Bevel vertices, Ctrl+V > Bevel vertices, Shift+Ctrl+B
        4. 【挤出】：向下挤出，E+Z
        5. 【塌陷边线&面】：Mesh > Delete > Collapse Edges & Faces, X > Collapse Edges & Faces
        6. 选择边：视频中选择方式可优化为按相似长度选择边，再循环边选择
            1. 选择一条斜边
            2. Select > Select Similar > Length, Shift+G > Length
            3. Select > Select Loops > Edge Loops
        7. 【边线倒角】：2段，Edge > Bevel Edges, Ctrl+B
        8. 移动倒角边：
            1. 减选一段：Select > Select More/Less > Less, Ctrl+-
            2. 【法向缩放】：往外，Mesh > Transform > Shrink Fatten, Alt+S
        9. 【塌陷边线&面】：将下方的顶点塌陷
        10. 退出编辑模式，平滑着色，添加细分修改器
        '''
        obj = self.__add_plane(size=total_size)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()

        select_list = [8, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
        bm = bmesh.from_edit_mesh(obj.data)
        self.__select_verts_list(bm, select_list)

        bpy.ops.mesh.select_nth()
        bpy.ops.mesh.vert_connect()

        bpy.ops.mesh.bevel(offset=hole_size, offset_pct=0, vertex_only=True)
        toggle_mesh_select_mode('FACE')
        bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={'value':(0, 0, -hole_depth), 'orient_type':'LOCAL'})
        bpy.ops.mesh.edge_collapse()

        self.__select_similar_func('LENGTH', bm, 220)
        bpy.ops.mesh.loop_multi_select(ring=False)

        bpy.ops.mesh.bevel(offset=edge_offset, offset_pct=0, segments=2, vertex_only=False)
        bpy.ops.mesh.select_less()
        bpy.ops.transform.shrink_fatten(value=edge_shrink)

        self.__finish_job(2)
        return(obj)

if '__main__' == __name__:
    method_class = ChesterfieldLeatherModel()
    obj_01 = method_class.type_01(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2)
    obj_02 = method_class.type_02(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2)
    obj_03 = method_class.type_03(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2)
    obj_04 = method_class.type_04(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2)
    obj_05 = method_class.type_05(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2)
    obj_06 = method_class.type_06(total_size=4, hole_height=0.2, hole_size=0.05, edge_offset=0.01, edge_shrink=0.02, edge_height=0.05)
    obj_07 = method_class.type_07(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2, edge_offset=0.01, edge_shrink=0.02)
    obj_08 = method_class.type_08(total_size=4, hole_height=0.2, hole_size=0.05, hole_depth=0.2, edge_offset=0.01, edge_shrink=-0.02)
    obj_01.location = (-7.5, 2.5, 0)
    obj_02.location = (-2.5, 2.5, 0)
    obj_03.location = (2.5, 2.5, 0)
    obj_04.location = (7.5, 2.5, 0)
    obj_05.location = (-7.5, -2.5, 0)
    obj_06.location = (-2.5, -2.5, 0)
    obj_07.location = (2.5, -2.5, 0)
    obj_08.location = (7.5, -2.5, 0)
